package com.monkeys.service.impl;

import com.monkeys.Utils.ALNS.ALNSMain;
import com.monkeys.Utils.ALNS.Parameter;
import com.monkeys.Utils.CalculateUtil;
import com.monkeys.pojo.DemandsList;
import com.monkeys.pojo.GoodsDemand;
import com.monkeys.pojo.Points;
import com.monkeys.pojo.Vehicle;
import com.monkeys.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;

/**
 * @author KuangTeam
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class WhileServiceImpl implements WhileService {

    private final GoodsService goodsService;
    private final VehicleService vehicleService;
    private final PointsService pointsService;
    private final DemandsListService demandsListService;
    private final SimulateService simulateService;

    //开始循环之前生成车辆和订单
    @Override
    public void generatedData(Long vehicleNum, Long goodsDemandNum) {
        //先删除上一次模拟的全部信息
        goodsService.deleteAll();
        vehicleService.deleteAll();
        pointsService.deleteAll();
        demandsListService.deleteAll();

        //创建订单和车辆
        goodsService.createGoodsById(goodsDemandNum);
        vehicleService.createVehicles(vehicleNum);

        //获取订单车辆信息
        List<Vehicle> vehicleList = vehicleService.getAllVehicles();
        List<GoodsDemand> goodsDemandList = goodsService.getAllGoodsDemands();

        //创建由订单生成的节点
        List<Points> pointsList = pointsService.createPoints(goodsDemandList);


        /*                              每个车辆根据ALNS获取订单流程表                                  */
        //得到结果集合
        List<List<Points>> resultList = ALNSMain.getALNSResult(vehicleList, pointsList);
        //更新车辆表、订单流程表
        for (int i = 0; i < resultList.size(); i++) {
            List<Points> pointList = resultList.get(i);
            Vehicle vehicle = vehicleList.get(i);
            DemandsList demandsList = new DemandsList();

            if (pointList != null) {
                String demandsListStr = CalculateUtil.getDemandsListStr(pointList);
                demandsList.setDemandsListStr(demandsListStr);
                demandsListService.insert(demandsList);
                vehicle.setDemandsListId(demandsList.getId());
                vehicleService.update(vehicle);
            }
        }

        //输出日志，便于观测结果，格式：No.0 : 基地 -> 1117 -> 1106 -> 1120 -> 1122 -> 基地
        //No.1 : 基地 -> 1111 -> 1118 -> 1107 -> 1121 -> 基地
        //No.2 : 基地 -> 1123 -> 1113 -> 1110 -> 基地
        //No.3 : 基地 -> 1105 -> 1109 -> 1124 -> 基地
        //No.4 : 基地 -> 1119 -> 1108 -> 1115 -> 1114 -> 1112 -> 基地
        //车辆号          goodsDemandId
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = 0; i < resultList.size(); ++i) {
            stringBuffer.append("No.").append(i).append(" : ");
            if (resultList.get(i).size() > 2) {
                for (int j = 0; j < resultList.get(i).size() - 1; ++j) {
                    stringBuffer.append(resultList.get(i).get(j).getId() == 0 ? "基地" : resultList.get(i).get(j).getGoodsDemandId()).append(" -> ");
                }
                stringBuffer.append(resultList.get(i).get(resultList.get(i).size() - 1).getId() == 0 ? "基地" : resultList.get(i).get(resultList.get(i).size() - 1).getGoodsDemandId());
            }
            stringBuffer.append("\n");
        }
        stringBuffer.append("\n").append("最短距离：").append(Parameter.Best_Value);
        log.info("ALNS Result:\n" + stringBuffer);
    }


    //开始循环,前端传递一次循环的时间(秒)还有每秒代表模拟的时间(小时)     比如传递1/0.1 代表一次循环1s，1s代表模拟的10min
    //分为两个部分，第一部分是数据准备，第二部分是循环模拟
    @Override
    public void startSimulation(Long second, Double hour) {
        //获取有订单的全部车辆信息
        List<Vehicle> vehicleList = vehicleService.getAllVehicles();
        //舍弃这轮模拟循环中没有订单的车辆
        vehicleList.removeIf(vehicle -> Objects.equals(demandsListService.getById(vehicle.getDemandsListId()).getDemandsListStr(), "0,0"));

        //一次循环模拟的虚假时间
        Double cycleTime = second * hour;

        // 异步遍历每个Vehicle对象，异步完成每辆车的模拟
        for (Vehicle vehicle : vehicleList) {
            vehicle.setActionId(1);    //1:运输中  2:无货休息中  3:运输休息中   4.意外中
            simulateService.simulate(vehicle, cycleTime, second);
        }
    }
}
